
local bit   = require 'bit'
local alien = require 'alien'
local sdl   = require 'bindings.sdl'
local debug = require 'debug'
local require, assert, print, pcall, tostring
    = require, assert, print, pcall, tostring
local floor, huge
    = math.floor, math.huge


module (...)


sdl.Init(sdl.INIT_TIMER + sdl.INIT_VIDEO)

local mouseOffsetX, mouseOffsetY = 0, 0
local mouseScaleX, mouseScaleY = 1, 1
local mouseMaxX, mouseMaxY = huge, huge

function declareMouseOffset(new_x, new_y)
  mouseOffsetX, mouseOffsetY = new_x, new_y
end
function declareMouseScale(new_x, new_y)
  mouseScaleX, mouseScaleY = new_x, new_y
end
function declareMouseMax(new_x, new_y)
  mouseMaxX, mouseMaxY = new_x, new_y
end

local _onclose
function onclose(func)
  _onclose = func
end

local wintitle = ''
function title(new)
  if new then
    wintitle = tostring(new)
    sdl.WM_SetCaption(wintitle, wintitle)
  else
    return wintitle
  end
end

local closing = false

local TIMEREVENT = sdl.USEREVENT

local timerEvent = sdl.Event:new()
timerEvent.type = TIMEREVENT

local timers = {}

TimerEvent_Callback = alien.callback(function(interval, param)
  sdl.PushEvent(param)
  return interval
end, sdl.NewTimerCallback_types)

function addInterval(func, ms)
  local timerEvent = sdl.UserEvent:new()
  timerEvent.type = TIMEREVENT
  local timerID = timerEvent():topointer()
  timerEvent.data1 = timerID
  local info = {func=func, timerEvent=timerEvent, sdlID = sdl.AddTimer(ms, TimerEvent_Callback, timerID)}
  timers[timerID] = info
  return timerID
end

function clearInterval(timerID)
  local info = timers[timerID]
  if not info then
    return false
  end
  timers[timerID] = nil
  return (sdl.RemoveTimer(info.sdlID) == 1)
end

local mouse, screen

local reuse_mbutton = sdl.MouseButtonEvent:new(nil)
local reuse_motion = sdl.MouseMotionEvent:new(nil)
local reuse_timer = sdl.UserEvent:new(nil)
local reuse_active = sdl.ActiveEvent:new(nil)
local reuse_resize = sdl.ResizeEvent:new(nil)

local EventTypeHandlers = {
  [sdl.QUIT] = function(evt)
    if _onclose then
      closing = not not _onclose()
    else
      closing = true
    end
  end;
  [sdl.MOUSEMOTION] = function(evt)
    reuse_motion(evt())
    local mouseX, mouseY = floor((reuse_motion.x + mouseOffsetX) * mouseScaleX), floor((reuse_motion.y + mouseOffsetY) * mouseScaleY)
    if (mouseX < 0) or (mouseY < 0) or (mouseX >= mouseMaxX) or (mouseY >= mouseMaxY) then
      mouseX, mouseY = nil, nil
    end
    mouse.declarePosition(mouseX, mouseY)
  end;
  [sdl.MOUSEBUTTONDOWN] = function(evt)
    reuse_mbutton(evt())
    mouse.declareButtonDown(reuse_mbutton.button)
  end;
  [sdl.MOUSEBUTTONUP] = function(evt)
    reuse_mbutton(evt())
    mouse.declareButtonUp(reuse_mbutton.button)
  end;
  [sdl.ACTIVEEVENT] = function(evt)
    reuse_active(evt())
    if (reuse_active.gain == 0) and (reuse_active.state == sdl.APPMOUSEFOCUS) then
      mouse.declarePosition(nil, nil)
    end
  end;
  [sdl.VIDEORESIZE] = function(evt)
    reuse_resize(evt())
    screen.declareWindowSize(reuse_resize.w, reuse_resize.h)
  end;
  [TIMEREVENT] = function(evt)
    reuse_timer(evt())
    local info = timers[reuse_timer.data1]
    if info then
      info.func()
    end
  end;
}

local using_opengl = false

function init_opengl()
  using_opengl = true
end

function init_sdl()
end

function open()
  screen = require 'guac.screen'
  mouse = require 'guac.mouse'
  local width, height = screen.dimensions()
  screen.init()
  local flags
  if using_opengl then
    sdl.GL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1)
    flags = bit.bor(sdl.OPENGL, sdl.RESIZABLE)
    screen.declareRedrawFunc(function()
      screen.internal_render()
      sdl.GL_SwapBuffers()
    end)
  else
    flags = bit.bor(sdl.ANYFORMAT, sdl.HWSURFACE, sdl.DOUBLEBUF, sdl.RESIZABLE)
    screen.declareRedrawFunc(function()
      screen.internal_render()
    end)
  end
  sdl.SetVideoMode(width, height, 32, flags)
  sdl.WM_SetCaption(wintitle, wintitle)
end

function close()
  sdl.QuitSubSystem(sdl.INIT_VIDEO + sdl.INIT_TIMER)
end

function loop()
  local evt = sdl.Event:new()
  while (sdl.WaitEvent(evt()) == 1) do
    local handler = EventTypeHandlers[evt.type]
    if handler then
      local success, result = pcall(handler, evt)
      if not success then
        print(result)
      end
    end
    if closing then
      break
    end
  end
  sdl.QuitSubSystem(sdl.INIT_VIDEO + sdl.INIT_TIMER)
end

if (alien.platform == 'windows') then

  local wminfo = sdl.SysWMinfo:new()

  function get_main_hwnd()
    if (sdl.GetWMInfo(wminfo()) == 1) then
      return wminfo.window
    end
  end
  
end

